/**
 +-------------------------------------------------------------------
 * FontScroll - 文字行向上滚动插件
 * 说明： 
 * 		1、改成通过$("li:first").appendTo("ul")的方式实现向上滚动
 * 		2、添加参数：
 * 		 	timeoutMethod
 * 		 	scrollBehavior
 * 		 	ul
 * 		 	li
 * 作者：konger123 （随枫的落叶 konger@126.com）
 * gitee：https://gitee.com/konger123/FontScroll
 * 
 * 原项目github：https://gitee.com/kzm/FontScroll
 * 原作者：kongzhim
 +-------------------------------------------------------------------
 * @version    1.0.2
 * @since      2020.09.05
 * @author     konger123 <konger@126.com>
 * @gitee     https://gitee.com/konger123/FontScroll
 +-------------------------------------------------------------------
 */
 
var FontScroll = (function() {
	
	/**
	 * 创建方法
	 * @param target 滚动目标div的ID或选择器
	 * @options 参数：
	 * 		time：每次滚动的时间间隔(单位：毫秒)，默认3000
	 * 		speed：滚动动画时间(单位：毫秒)，数值越小滚动动画速度越快，默认500
	 * 		timeoutMethod: 延时时间方式：interval-使用setInverval处理；timeout-使用setTimeout处理
	 * 		rowClass：滚动选中行的样式，默认：fontColor
	 * 		num：滚动选中的行序号，默认：0 表示第一行
	 * 		skip：滚动时上面留白显示的行数，默认0
	 * 		scrollBehavior: 滚动动画与行样式的先后顺序：rowFirst先加行样式，再动画滚动；scrollFirst先动画滚动，再加行样式
	 * 		pauseOnHover：鼠标经过时是否暂停，默认：true
	 *		startOnInit：初始化后是否自动运行，默认：true
	 * 		init：是否默认初始化，默认true
	 * 		ul: 滚动的行的外部容器（选择器），用于获取滚动区域的实际高度
	 * 		li: 滚动行（选择器）
	 */
	var FontScroll = function(target, options) {
		var _this = this;
        this.version = '1.0.2';
		this.defaults = {
            time: 3000,	//每次滚动的时间间隔(单位：毫秒)，默认3000
			speed: 500,	//滚动动画时间(单位：毫秒)，数值越小滚动动画速度越快，默认500
			timeoutMethod: 'interval', //延时时间方式：interval-使用setInverval处理；timeout-使用setTimeout处理
            rowClass: 'fontColor', //滚动选中行的样式，默认：fontColor
            num: 0,  //滚动时选中的行序号，默认：0 表示第一行
			skip: 0,//滚动时上面留白显示的行数，默认0
			scrollBehavior: 'rowFirst', //滚动动画与行样式的先后顺序：rowFirst先加行样式，再动画滚动；scrollFirst先动画滚动，再加行样式
            pauseOnHover: true,	//鼠标经过时是否暂停，默认：true
			startOnInit: true,	//初始化后是否自动运行，默认：true
			init: true,	//是否默认初始化
			ul: 'ul', //滚动的行的外部容器（选择器），用于获取滚动区域的实际高度
			li: 'li', //滚动行（选择器）
        };
        this.options = $.extend({}, this.defaults, options);
		if(this.options.speed>=this.options.time) {//防止给定的动画时间不足
			this.options.speed = this.options.time - 10;
		}
		if(this.options.num > this.options.skip) {//防止越界
			this.options.num = this.options.skip;
		}
		
        this.target = target;
        this.el = (typeof target === 'string') ? $(target) : $(target);
		this.ul = null;//ul对象
		this.li = null;
		this.length = 0;//li的个数
		
		this.elH = 0;
		this.ulH = 0;
		
        this.timeID = null;//timer的ID
		this.cursor = 0;
		this.inited = false;
		
		if(this.options.init) {
			this.init();
		}
		
		if(this.options.startOnInit) {
			this.start();
		}
	}
	
	/**
	 * 初始化
	 */
	FontScroll.prototype.init = function() {
		if(this.el==undefined || this.el==null) {
			throw new Error("初始化失败，对象是否已被销毁？请检查！");
		}
		
		if(this.inited) {
			return this;
		}
		
		this.ul = this.el.find(this.options.ul).first();//ul对象
		if(this.ul == null || this.ul.length < 1) {
			throw new Error("初始化失败，未找到'"+this.options.ul+"'对象");
		}
		this.li = this.ul.find(this.options.li);
		this.length = this.li.length;
		
		this.elH = this.el.height();
		this.ulH = this.ul.height();
		
        this.timeID = null;//timer的ID
		this.cursor = this.options.num;
		
		if(this.length > 0) {
			this.options.num = this.options.num % this.length;
			this.options.skip = this.options.skip % this.length;
			if(this.options.num > this.options.skip) {
				this.options.num = this.options.skip;
			}
			
			this.cursor = this.options.num % this.length;
			
			this.li.removeClass(this.options.rowClass);
			this.li.eq(this.cursor).addClass(this.options.rowClass);
		}
		
		this.inited = true;
		return this;
	}
	
	/**
	 * 更新参数
	 * @param options 新的参数
	 * @param immediatelyEffect 新参数是否立即生效
	 */
	FontScroll.prototype.updateOptions = function(options, immediatelyEffect) {
		this.options = $.extend({}, this.options, options);
		
		if(immediatelyEffect) {
			if(this.timeID!=null) {
				//如果正在运行的话，使新参数立即生效
				this.pause();
				this.resume();
			}
		}
		return this;
	}
	
	/**
	 * 开始运行
	 */
	FontScroll.prototype.start = function() {
		if(this.timeID != null) {
			return this;
		}
		if(this.length < 1) {
			return this;
		}
		if(!this.inited) {
			this.init();
		}
		
		var _this = this;
		
		if(_this.options.timeoutMethod == 'interval') {
			_this.timeID = setInterval(function() {
				_this.up();
			}, _this.options.time);
		} else {
			_this.timeID = setTimeout(function() {
				_this.up();
			}, _this.options.time);
		}
        
		if(_this.options.pauseOnHover) {
			_this.ul.hover(function(){
				if(_this.timeID != null) {
					if(_this.options.timeoutMethod == 'interval') {
						clearInterval(_this.timeID);
					} else {
						clearTimeout(_this.timeID);
					}
					_this.timeID = null;
				}
			},function(){
				if(_this.options.timeoutMethod == 'interval') {
					_this.timeID = setInterval(function() {
						_this.up();
					}, _this.options.time);
				} else {
					_this.timeID = setTimeout(function() {
						_this.up();
					}, _this.options.time);
				}
			});
		}
        
        return this;
	}
	
	/**
	 * 继续
	 * @returns {Number} timeID
	 */
	FontScroll.prototype.resume = function() {
        return this.start();
	}
	
	/**
	 * 向上滚动一行
	 * @param resetDelay 当延时方法为timeout时，是否在滚动后要重新计算延时
	 */
	FontScroll.prototype.up = function(resetDelay) {
		if(!this.inited) {
			this.init();
		}
		
		var _this = this;
		
		if(_this.length > 0) {
			_this.cursor++;
			_this.cursor = _this.cursor % _this.length;
			
			var liH = _this.li.eq(0).outerHeight();
			
			//先加行样式，后滚动
			if(_this.options.scrollBehavior == "rowFirst") {
				_this.li.removeClass(_this.options.rowClass);
				_this.li.eq(_this.options.skip+1).addClass(_this.options.rowClass);
			}
			
			var rowSelectedIndex = -1;
			_this.li.each(function(i, o) {
				if(rowSelectedIndex < 0 && $(o).hasClass(_this.options.rowClass)) {
					rowSelectedIndex = i;
					return;
				}
			});
			if(rowSelectedIndex > -1 && rowSelectedIndex < _this.options.skip) {//选中行还没到skip的位置
				_this.li.removeClass(_this.options.rowClass);
				_this.li.eq(rowSelectedIndex+1).addClass(_this.options.rowClass);
			} else {
				//先加行样式，后滚动
				if(_this.options.scrollBehavior == "rowFirst") {
					_this.li.removeClass(_this.options.rowClass);
					_this.li.eq(_this.options.skip+1).addClass(_this.options.rowClass);
				}
				
				//滚动动画
				_this.ul.animate({marginTop: '-'+liH}, _this.options.speed, function() {
					_this.ul.animate({marginTop: 0}, 10, function() {
						_this.li.eq(0).appendTo(_this.li.eq(0).parent());
						_this.li = _this.ul.find(_this.options.li);
						//先滚动，后加行样式
						if(_this.options.scrollBehavior != "rowFirst") {
							_this.li.removeClass(_this.options.rowClass);
							_this.li.eq(_this.options.skip).addClass(_this.options.rowClass);
						}
					});
				});
			}
		}
		
		if(_this.timeID != null) {
			if(_this.options.timeoutMethod == 'timeout') {
				clearTimeout(_this.timeID);
				_this.timeID = setTimeout(function() {
					_this.up();
				}, _this.options.time);
			} else {
				if(resetDelay) {
					clearInterval(_this.timeID);
					_this.timeID = setInterval(function() {
						_this.up();
					}, _this.options.time);
				}
			}
		}
		return this;
	}
	
	/**
	 * 暂停
	 */
	FontScroll.prototype.pause = function() {
		if(!this.inited) {
			this.init();
		}
		
		if(this.options.pauseOnHover) {
			//取消hover事件
			this.ul.unbind('mouseenter').unbind('mouseleave');
		}
		if(this.timeID != null) {
			if(this.options.timeoutMethod == 'interval') {
				clearInterval(this.timeID);
			} else {
				clearTimeout(this.timeID);
			}
			this.timeID = null;
		}
		return this;
	}
	
	/**
	 * 停止
	 */
	FontScroll.prototype.stop = function() {
		if(!this.inited) {
			this.init();
		}
		
		var _this = this;

		//取消事件
		if(this.options.pauseOnHover) {
			//取消hover事件
			this.ul.unbind('mouseenter').unbind('mouseleave');
		}
		//停止滚动
		if(this.timeID != null) {
			if(this.options.timeoutMethod == 'interval') {
				clearInterval(this.timeID);
			} else {
				clearTimeout(this.timeID);
			}
			this.timeID = null;
		}
		
		//滚动还原
		this.cursor = this.options.num;
		
		if(this.length > 0) {
			this.cursor = this.options.num % this.length;
			
			this.li.removeClass(this.options.rowClass);
			this.li.eq(this.cursor).addClass(this.options.rowClass);
		}
		
		this.ul.animate({marginTop: '0'}, 10);
		
		return this;
	}
	
	/**
	 * 销毁对象。销毁后对象会变成"{}"
	 */
	FontScroll.prototype.destroy = function() {
		this.stop();
		for(var a in this) {
			delete this[a];
		}
	}
	
	/**
	 * 添加li
	 * @param o 要添加的li对象。可以是"<li>...</li>"这样的字符串，也可以是dom对象
	 */
	FontScroll.prototype.addLi = function(o) {
		if(!this.inited) {
			throw new Error("清先初始化后再进行此操作！");
		}
		
		var dom = $(o);
		if(this.length < 1) {
			this.ul.append(dom);
		} else {
			this.cursor = this.cursor % this.length;
			dom.insertAfter(this.li.eq(this.length - this.cursor + this.options.skip - 1));
		}
		this.li = this.ul.find(this.options.li);
		this.length = this.li.length;
		
		this.elH = this.el.height();
		this.ulH = this.ul.height();
		
		return this;
	}
	
	/**
	 * 移除li
	 * @param selector 要移除li的选择器。如"#li_1"、"li:first"、"li:last"、"li:eq(2)"等等
	 */
	FontScroll.prototype.removeLi = function(selector) {
		if(!this.inited) {
			throw new Error("清先初始化后再进行此操作！");
		}
		
		this.ul.find(selector).remove();
		this.li = this.ul.find(this.options.li);
		this.length = this.li.length;
		
		this.elH = this.el.height();
		this.ulH = this.ul.height();
		
		if(this.cursor == this.length - 1) {
			this.cursor = 0;
		}
		
		return this;
	}
	
	/**
	 * 加法函数，用来得到精确的加法结果
	 * 说明：javascript的加法结果会有误差，在两个浮点数相加的时候会比较明显。这个函数返回较为精确的加法结果。
	 * 调用：accAdd(arg1,arg2)
	 * 参数：arg1被加数，arg2加数
	 * 返回值：arg1加上arg2的精确结果
	 */
	var accAdd = function(arg1, arg2) {
		var r1, r2, m;
		try {
			r1 = arg1.toString().split(".")[1].length;
		} catch (e) {
			r1 = 0;
		}
		try {
			r2 = arg2.toString().split(".")[1].length;
		} catch (e) {
			r2 = 0;
		}
		m = Math.pow(10, Math.max(r1, r2));
		return (arg1 * m + arg2 * m) / m;
	}
	
	return FontScroll;
}());